package org.KeyCrafter.key;

import org.KeyCrafter.cmd.HashCommand;
import org.KeyCrafter.obj.HashObj;
import org.KeyCrafter.obj.KeyCrafterObjFactory;
import org.KeyCrafter.obj.KeyCrafterObject;
import org.KeyCrafter.obj.type.HashType;
import org.KeyCrafter.server.session.ClientSession;
import org.KeyCrafter.util.SharedReply;

import static org.KeyCrafter.util.ReplyUtil.*;

public class HashKey implements HashCommand {

    @Override
    public void hsetCommand(ClientSession c) {
        boolean update;
        HashObj o;
        // 判断是否存在来决定取出或者新建hash对象
        if ((o = lookupWriteOrCreate(c,c.argv(1))) == null) {
            return;
        }
        // 设置 field 和 value 到 hash
        update = o.getValue().set(c.argv(2),c.argv(3)) == 0;
        // 返回状态：显示 field-value 对是新添加还是更新
        addReply(c, update ? SharedReply.cZero :SharedReply.cOne);
        // 将服务器设为脏
        c.db().incrDirty();
    }

    @Override
    public void hgetCommand(ClientSession c) {
        KeyCrafterObject o;
        o = c.db().lookupKeyRead(c.argv(1));
        if (o == null) {
            addReply(c,SharedReply.nullBulk);
            return;
        } else if (!(o instanceof HashObj)) {
            addReply(c,SharedReply.wrongTypeErr);
            return;
        }
        // 取出并返回域的值
        addHashFieldToReply(c,(HashObj)o,c.argv(2));
    }

    @Override
    public void hmsetCommand(ClientSession c) {
        int argc = c.argc();
        // field-value 参数必须成对出现
        if ((argc % 2) == 1) {
            addReplyError(c,"wrong number of arguments for HMSET");
            return;
        }
        // 取出或新创建哈希对象
        HashObj o;
        if ((o = lookupWriteOrCreate(c,c.argv(1))) == null) {
            return;
        }
        HashType hashType = o.getValue();
        // 遍历并设置所有 field-value 对
        for (int i = 2; i < argc; i+=2) {
            hashType.set(c.argv(i),c.argv(i+1));
        }
        // 向客户端发送回复
        addReply(c,SharedReply.ok);
        // 将数据库设为脏
        c.db().incrDirty();
    }

    @Override
    public void hmgetCommand(ClientSession c) {
        // 取出哈希对象
        KeyCrafterObject o = c.db().lookupKeyRead(c.argv(1));
        if (o == null) {
            addReply(c,SharedReply.nullBulk);
        }
        // 对象存在，检查类型
        if (!(o instanceof HashObj)) {
            addReply(c, SharedReply.wrongTypeErr);
            return;
        }
        // 获取多个 field 的值
        // addReply 不是追加型是替换的（和 redis 定义的不同），
        // 所以不能用 addHashFieldToReply （后期考虑修改 addReply 函数的实现）
        int argc = c.argc();
        String[] reply = new String[argc - 2];
        HashType hashType = ((HashObj) o).getValue();
        int j = 0;
        for (int i = 2; i < argc; i++) {
            reply[j++] = (String) hashType.get(c.argv(i));
        }
        addReplyArray(c,reply);
    }

    /**
     * 存储链表命令函数
     * 存在则找出值，不存在则新建
     */
    private static HashObj lookupWriteOrCreate(ClientSession c, String key) {
        KeyCrafterObject o = c.db().lookupKeyWrite(key);
        // 对象不存在，创建新的
        if (o == null ) {
            o = KeyCrafterObjFactory.newHashObj();
            c.db().dbAdd(key,o);
        } else {
            // 对象存在，检查类型
            if (!(o instanceof HashObj)) {
                addReply(c, SharedReply.wrongTypeErr);
                return null;
            }
        }
        // 返回对象
        return (HashObj) o;
    }
    /**
     * 辅助函数：
     *  将哈希 field 值添加到恢复中
     */
    private static void addHashFieldToReply(ClientSession c, HashObj o, String field)  {
        // 对象不存在
        if (o == null) {
            addReply(c, SharedReply.nullBulk);
            return;
        }
        // 取出值
        Object val = o.getValue().get(field);
        if (val == null) {
            addReply(c, SharedReply.nullBulk);
        } else {
            addReplyBulk(c,(String) val);
        }
    }
}
